﻿namespace Codeplex.KinkumaFramework
{
    using System;
    using System.Linq;
    using System.Reactive.Disposables;
    using System.Reactive.Linq;
    using System.Reactive.Subjects;
    using Codeplex.KinkumaFramework.Utils;

    /// <summary>
    /// ReactiveProperty#ObserveErrorChanged's container.
    /// </summary>
    public class ReactiveErrorsContainer : IObservable<bool>, IDisposable
    {
        private bool latestValue;
        private readonly Subject<bool> source = new Subject<bool>();
        private readonly IObservable<bool> errorChanged;
        private readonly SingleAssignmentDisposable disposable = new SingleAssignmentDisposable();

        public ReactiveErrorsContainer(bool defaultValue, params IObservable<object>[] observeErrorChanged)
        {
            if (DesignTimeUtils.IsInDesignMode)
            {
                // no op
                return;
            }

            this.latestValue = defaultValue;

            if (observeErrorChanged.Length == 0)
            {
                this.errorChanged = Observable.Never<bool>();
                return;
            }

            this.errorChanged = observeErrorChanged
                .Select(obs => obs.Select(o => o == null))
                .Aggregate(
                    (xs, ys) => xs.CombineLatest(ys, (x, y) => x && y));
            this.disposable.Disposable = this.errorChanged.Subscribe(
                b => 
                {
                    this.latestValue = b;
                    source.OnNext(b);
                });
        }

        public ReactiveErrorsContainer(params IObservable<object>[] observeErrorChanged) : this(false, observeErrorChanged)
        {
        }

        public IDisposable Subscribe(IObserver<bool> observer)
        {
            var ret = this.source.Subscribe(observer);
            observer.OnNext(latestValue);
            return ret;
        }

        public void Dispose()
        {
            if (disposable.IsDisposed)
            {
                return;
            }

            this.source.OnCompleted();
            this.source.Dispose();
            this.disposable.Dispose();
        }

        public bool IsValid
        {
            get
            {
                return this.latestValue;
            }
        }

        public bool HasErrors
        {
            get
            {
                return !this.IsValid;
            }
        }
    }
}
